const multiparty = require("multiparty");
const path = require("path");
const fse = require("fs-extra");

const extractExt = filename =>
    filename.slice(filename.lastIndexOf("."), filename.length); // 提取后缀名

const UPLOAD_DIR = path.resolve(__dirname, "..", "target"); // 大文件存储目录

const pipeStream = (path, writeStream) =>
    new Promise(resolve => {
        const readStream = fse.createReadStream(path);
        readStream.on("end", () => {
            fse.unlinkSync(path);
            resolve();
        });
        readStream.pipe(writeStream);
    });

// 合并切片
const mergeFileChunk = async (filePath, filename, size) => {
    const chunkDir = path.resolve(UPLOAD_DIR, filename);
    const chunkPaths = await fse.readdir(chunkDir);
    chunkPaths.sort((a, b) => a.split("-")[1] - b.split("-")[1]);
    await Promise.all(
        chunkPaths.map((chunkPath, index) =>
            pipeStream(
                path.resolve(chunkDir, chunkPath),
                fse.createWriteStream(filePath, {
                    start: index * size,
                    end: (index + 1) * size
                })
            )
        )
    );
    fse.rmdirSync(chunkDir);
};

const resolvePost = req =>
    new Promise(resolve => {
        let chunk = "";
        req.on("data", data => {
            chunk += data;
        });
        req.on("end", () => {
            resolve(JSON.parse(chunk));
        });
    });

module.exports = class {
    // 合并切片
    async handleMerge(req, res) {
        const data = await resolvePost(req)
        const {filename, size} = data
        let mergePath = path.resolve(UPLOAD_DIR, 'merge')
        if (!fse.existsSync(mergePath)) {
            fse.mkdirSync(mergePath)
        }
        const filePath = path.resolve(mergePath, `${filename}`);
        await mergeFileChunk(filePath, filename, size)
        res.end(
            JSON.stringify({
                code: 0,
                message: 'file merged success'
            })
        )
    }

    // 处理切片
    async handleFormData(req, res) {
        const multipart = new multiparty.Form()
        multipart.parse(req, async (err, fields, files) => {
            if (err) {
                return
            }

            const [chunk] = files.chunk
            const [hash] = fields.hash
            const [filename] = fields.filename
            const chunkDir = path.resolve(UPLOAD_DIR, filename)

            if (!fse.existsSync(chunkDir)) {
                await fse.mkdirs(chunkDir)
            }

            await fse.move(chunk.path, `${chunkDir}/${hash}`)
            res.end('received file chunk')
        })

    }

    // 验证是否已上传/已上传切片下标
    async handleVerifyUpload(req, res) {
        const data = await resolvePost(req);
        const {fileHash, filename} = data;
        const ext = extractExt(filename);
        const filePath = path.resolve(UPLOAD_DIR, `${fileHash}${ext}`);
        if (fse.existsSync(filePath)) {
            res.end(
                JSON.stringify({
                    shouldUpload: false
                })
            );
        } else {
            res.end(
                JSON.stringify({
                    shouldUpload: true,
                    uploadedList: await createUploadedList(fileHash)
                })
            );
        }
    }
};
